<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';

import cytoscape from 'cytoscape';
import fcose from 'cytoscape-fcose';

const props = defineProps<{ elements: any }>();
const emit = defineEmits(['nodeClick']);

cytoscape.use(fcose);

const containerRef = ref<HTMLDivElement | null>(null);
const cyRef = ref<cytoscape.Core | null>(null);
const searchText = ref('');

// 生成随机颜色函数
const generateRandomColor = () => {
  const hue = Math.floor(Math.random() * 360);
  const saturation = 60 + Math.random() * 30;
  const lightness = 50 + Math.random() * 30;
  return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
};

// 为元素添加随机颜色和小写标签
const processElements = (elements: any) => {
  return {
    nodes: elements.nodes.map((node: any) => {
      return {
        ...node,
        data: {
          ...node.data,
          label: node.data.label ? node.data.label.toLowerCase() : '',
          color: generateRandomColor(), // 每个节点独立随机颜色
        },
        position: {
          x:
            Math.random() * (containerRef.value?.clientWidth || 800) -
            (containerRef.value?.clientWidth || 800) * 0.5,
          y:
            Math.random() * (containerRef.value?.clientHeight || 600) -
            (containerRef.value?.clientHeight || 600) * 0.5,
        },
      };
    }),
    edges: elements.edges,
  };
};

// 检测孤立节点（没有连接的节点）
const getIsolatedNodes = (cy: cytoscape.Core) => {
  return cy.nodes().filter((node) => node.connectedEdges().empty());
};

const initGraph = () => {
  if (!containerRef.value) return;

  const processedElements = processElements(props.elements);

  const cy = cytoscape({
    container: containerRef.value,
    elements: processedElements,
    layout: {
      name: 'fcose',
      animate: true,
      animationDuration: 1000,
      randomize: true,
      nodeRepulsion: 10_000, // 增加排斥力
      idealEdgeLength: 100,
      nodeSeparation: 50,
      gravity: 0.1, // 减小重力
      numIter: 3000,
    },
    style: [
      {
        selector: 'node',
        style: {
          label: 'data(label)',
          'background-color': 'data(color)',
          'background-image': 'data(icon)',
          'background-fit': 'cover',
          'background-opacity': 0.9,
          'border-color': '#fff',
          'border-width': 1,
          'text-valign': 'center',
          'text-halign': 'center',
          color: '#2D3748',
          'font-family': 'Times New Roman, serif', // 新罗马字体
          'font-size': 14,
          'font-weight': 'normal', // 不加粗
          width: 60, // 增大节点尺寸
          height: 60,
          shape: 'ellipse',
          'text-wrap': 'wrap',
          'text-max-width': '80px',
          'text-overflow-wrap': 'anywhere',
        },
      },
      {
        selector: 'edge',
        style: {
          width: 1.5,
          'line-color': 'rgba(100, 100, 100, 0.4)',
          'target-arrow-color': 'rgba(100, 100, 100, 0.4)',
          'target-arrow-shape': 'triangle',
          'curve-style': 'bezier',
          'arrow-scale': 1.2,
          opacity: 0.7,
        },
      },
      {
        selector: 'node:selected',
        style: {
          'border-color': '#FFD700',
          'border-width': 3,
          'shadow-color': '#FFD700',
          'shadow-blur': 15,
          'shadow-opacity': 0.8,
        },
      },
      {
        selector: 'node[degree > 5]', // 高度连接的节点
        style: {
          width: 'mapData(degree, 5, 20, 70, 120)',
          height: 'mapData(degree, 5, 20, 70, 120)',
          'font-size': 'mapData(degree, 5, 20, 14, 18)',
        },
      },
    ],
  });

  // 对孤立节点应用随机布局
  const isolatedNodes = getIsolatedNodes(cy);
  if (isolatedNodes.length > 0) {
    isolatedNodes
      .layout({
        name: 'random',
        animate: true,
        animationDuration: 800,
        fit: false,
      })
      .run();
  }

  // 交互效果
  cy.on('tap', 'node', (evt) => {
    const node = evt.target;
    emit('nodeClick', node.data());

    node
      .animate({
        style: {
          width: '+=20',
          height: '+=20',
        },
        duration: 200,
      })
      .animate({
        style: {
          width: '-=20',
          height: '-=20',
        },
        duration: 200,
      })
      .play();
  });

  cy.on('mouseover', 'node', (evt) => {
    const node = evt.target;
    node.style({
      'border-width': 2,
      'shadow-color': node.style('background-color'),
      'shadow-blur': 20,
      'shadow-opacity': 0.5,
    });

    node.connectedEdges().style({
      'line-color': '#FFA500',
      'target-arrow-color': '#FFA500',
      opacity: 0.9,
      width: 2.5,
    });
  });

  cy.on('mouseout', 'node', (evt) => {
    const node = evt.target;
    node.style({
      'border-width': 1,
      'shadow-blur': 0,
    });

    node.connectedEdges().style({
      'line-color': 'rgba(100, 100, 100, 0.4)',
      'target-arrow-color': 'rgba(100, 100, 100, 0.4)',
      opacity: 0.7,
      width: 1.5,
    });
  });

  cyRef.value = cy;
};

onMounted(() => {
  initGraph();
});

watch(
  () => props.elements,
  () => {
    if (cyRef.value) {
      cyRef.value.elements().remove();
      const processedElements = processElements(props.elements);
      cyRef.value.add(processedElements);

      // 主布局
      cyRef.value
        .layout({
          name: 'fcose',
          animate: true,
          animationDuration: 1000,
          randomize: true,
          nodeRepulsion: 10_000,
          idealEdgeLength: 100,
          nodeSeparation: 50,
          gravity: 0.1,
          numIter: 3000,
        })
        .run();

      // 对孤立节点应用随机布局
      setTimeout(() => {
        const isolatedNodes = getIsolatedNodes(cyRef.value!);
        if (isolatedNodes.length > 0) {
          isolatedNodes
            .layout({
              name: 'random',
              animate: true,
              animationDuration: 800,
              fit: false,
            })
            .run();
        }
      }, 100);
    }
  },
  { deep: true },
);

const zoomIn = () =>
  cyRef.value?.zoom({
    level: cyRef.value.zoom() * 1.2,
    renderedPosition: {
      x: cyRef.value.width() / 2,
      y: cyRef.value.height() / 2,
    },
  });

const zoomOut = () =>
  cyRef.value?.zoom({
    level: cyRef.value.zoom() * 0.8,
    renderedPosition: {
      x: cyRef.value.width() / 2,
      y: cyRef.value.height() / 2,
    },
  });

const randomizeLayout = () => {
  if (!cyRef.value) return;

  // 重新分配随机颜色
  cyRef.value.nodes().forEach((node) => {
    node.style('background-color', generateRandomColor());
  });

  // 主布局
  cyRef.value
    .layout({
      name: 'fcose',
      animate: true,
      animationDuration: 800,
      randomize: true,
      nodeRepulsion: 8000 + Math.random() * 4000,
      idealEdgeLength: 50 + Math.random() * 100,
      nodeSeparation: 30 + Math.random() * 70,
      gravity: 0.05 + Math.random() * 0.2,
      numIter: 2000,
    })
    .run();

  // 对孤立节点应用随机布局
  setTimeout(() => {
    const isolatedNodes = getIsolatedNodes(cyRef.value!);
    if (isolatedNodes.length > 0) {
      isolatedNodes
        .layout({
          name: 'random',
          animate: true,
          animationDuration: 600,
          fit: false,
        })
        .run();
    }
  }, 100);
};

const searchNode = () => {
  if (!cyRef.value) return;
  const keyword = searchText.value.toLowerCase();
  const found = cyRef.value
    .nodes()
    .filter((node) => node.data('label').toLowerCase().includes(keyword));

  if (found.length > 0) {
    cyRef.value.animate({
      fit: { eles: found, padding: 100 },
      duration: 800,
    });
    cyRef.value.nodes().unselect();
    found.select();

    found
      .animate({
        style: {
          'border-color': '#FFD700',
          'border-width': 3,
          'shadow-color': '#FFD700',
          'shadow-blur': 25,
        },
        duration: 300,
      })
      .play();
  }
};

const resetView = () => {
  cyRef.value?.animate({
    fit: { eles: cyRef.value.elements(), padding: 50 },
    duration: 800,
  });
};
</script>

<template>
  <div class="relative h-full w-full">
    <!-- 工具栏 -->
    <div
      class="absolute left-4 top-4 z-10 flex gap-3 rounded-2xl bg-white/80 p-3 shadow-lg backdrop-blur-sm transition-all hover:bg-white/95"
    >
      <button
        @click="zoomIn"
        class="flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-blue-500 to-blue-600 text-white shadow hover:from-blue-600 hover:to-blue-700"
        title="放大"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          class="h-5 w-5"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            stroke-linecap="round"
            stroke-linejoin="round"
            stroke-width="2"
            d="M12 6v6m0 0v6m0-6h6m-6 0H6"
          />
        </svg>
      </button>
      <button
        @click="zoomOut"
        class="flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-blue-500 to-blue-600 text-white shadow hover:from-blue-600 hover:to-blue-700"
        title="缩小"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          class="h-5 w-5"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            stroke-linecap="round"
            stroke-linejoin="round"
            stroke-width="2"
            d="M20 12H4"
          />
        </svg>
      </button>
      <button
        @click="resetView"
        class="flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-gray-500 to-gray-600 text-white shadow hover:from-gray-600 hover:to-gray-700"
        title="重置视图"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          class="h-5 w-5"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            stroke-linecap="round"
            stroke-linejoin="round"
            stroke-width="2"
            d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"
          />
        </svg>
      </button>
      <button
        @click="randomizeLayout"
        class="flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-purple-500 to-purple-600 text-white shadow hover:from-purple-600 hover:to-purple-700"
        title="随机化布局"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          class="h-5 w-5"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            stroke-linecap="round"
            stroke-linejoin="round"
            stroke-width="2"
            d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
          />
        </svg>
      </button>

      <div class="relative">
        <input
          v-model="searchText"
          @keyup.enter="searchNode"
          placeholder="搜索节点..."
          class="h-10 rounded-full border-0 bg-gray-100 pl-4 pr-10 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
        />
        <button
          @click="searchNode"
          class="absolute right-0 top-0 flex h-10 w-10 items-center justify-center rounded-full text-gray-500 hover:text-blue-600"
          title="搜索"
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            class="h-5 w-5"
            fill="none"
            viewBox="0 0 24 24"
            stroke="currentColor"
          >
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              stroke-width="2"
              d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
            />
          </svg>
        </button>
      </div>
    </div>

    <!-- Cytoscape 容器 -->
    <div
      ref="containerRef"
      class="h-full w-full rounded-xl border border-gray-200 shadow-inner"
    ></div>

    <!-- 底部信息栏 -->
    <div
      class="absolute bottom-4 left-4 z-10 rounded-2xl bg-white/80 px-4 py-2 text-sm text-gray-600 shadow backdrop-blur-sm"
    >
      点击节点查看详情 • 滚轮缩放 • 拖动平移 • 点击<i
        class="mx-1 text-purple-600"
      >
        随机化
      </i>
      按钮改变颜色和布局
    </div>
  </div>
</template>

<style scoped>
/* 节点浮动动画 */
@keyframes float {
  0% {
    transform: translateY(0);
  }

  50% {
    transform: translateY(-5px);
  }

  100% {
    transform: translateY(0);
  }
}

input::placeholder {
  color: #9ca3af;
  opacity: 1;
}

input:focus {
  box-shadow: 0 0 0 2px rgb(59 130 246 / 50%);
}

div[ref='containerRef'] {
  background: linear-gradient(135deg, #f9fafb 25%, #f3f4f6 100%);
}

:deep(.cy-node:hover) {
  z-index: 9999 !important;
  animation: float 3s ease-in-out infinite;
}
</style>
